package tetris.game;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public class Game {
	/** The height of the Well (playing-field) */
	public static final int WELL_HEIGHT = 20;
	/** The width of the Well (playing-field) */
	public static final int WELL_WIDTH = 10;
	/** Boolean value used to enter and exit the game loop */
	boolean m_GameRunning;
	/** Boolean value checking for input from user */
	public boolean m_input;
	/** A Tetrad. The only active piece the player is using */
	Tetrad m_Piece = new Tetrad();
	/** A Tetrad. The next piece the player will be using */
	Tetrad m_NextPiece = new Tetrad();
	
	//TODO Make multiple next pieces(4 in total)
	//TODO Put in a m_HoldPiece
	
	/** The Well, or playing-field, this is where the blocks stack up */ 
	WellBlock [][] m_Well = new WellBlock[WELL_HEIGHT][WELL_WIDTH];
	/** The Score Keeper */
	int m_Score;
	BufferedImage m_HappyFace = null;
	BufferedImage m_DisappointedFace = null;
	BufferedImage m_SadFace = null;
	
	
	Tetrad m_OBlock = new Tetrad();
	Tetrad m_IBlock = new Tetrad();
	Tetrad m_TBlock = new Tetrad();
	Tetrad m_SBlock = new Tetrad();
	Tetrad m_ZBlock = new Tetrad();
	Tetrad m_LBlock = new Tetrad();
	Tetrad m_JBlock = new Tetrad();

	/**
	 * Constructor
	 * Sets m_GameRunning to true<br>
	 * Sets m_Score to 0
	 */
	public Game()
	{
		m_GameRunning = true;
		m_input = false;
		m_Score = 0;
		m_OBlock.CreatePiece(0);
		m_IBlock.CreatePiece(1);
		m_TBlock.CreatePiece(2);
		m_SBlock.CreatePiece(3);
		m_ZBlock.CreatePiece(4);
		m_LBlock.CreatePiece(5);
		m_JBlock.CreatePiece(6);
		m_Piece.CreatePiece((int) (Math.random()*7));
		m_NextPiece.CreatePiece((int)(Math.random()*7));
		m_NextPiece.m_X = 11; m_NextPiece.m_Y = 0;
		
		//fill Well with data because Java doesn't explicitly do so
		for(int y = 0; y < WELL_HEIGHT; ++y)
		{
			for(int x = 0; x < WELL_WIDTH; ++x)
			{
				m_Well[y][x] = new WellBlock();
			}
		}
		
		try { m_HappyFace = ImageIO.read(new File("happyBlockmask.png")); } catch (IOException e) {}
		try { m_DisappointedFace = ImageIO.read(new File("medBlockmask.png")); } catch (IOException e) {}
		try { m_SadFace = ImageIO.read(new File("sadBlockmask.png")); } catch (IOException e) {}
	}
	
	/**
	 * Used to keep the game loop looping
	 * @return the value of m_GameRunning
	 */
	public boolean isRunning()
	{
		return m_GameRunning;
	}

	/**
	 * Sets the current tetrad to a copy of the next<br>
	 * Changes the next piece into a new piece
	 */
	public void SwapPieces()
	{
		m_Piece.Copy(m_NextPiece);
		switch((int)(Math.random()*7))
		{
		case 0:
			m_NextPiece.Copy(m_OBlock);
			break;
		case 1:
			m_NextPiece.Copy(m_IBlock);
			break;
		case 2:
			m_NextPiece.Copy(m_TBlock);
			break;
		case 3:
			m_NextPiece.Copy(m_SBlock);
			break;
		case 4:
			m_NextPiece.Copy(m_ZBlock);
			break;
		case 5:
			m_NextPiece.Copy(m_LBlock);
			break;
		case 6:
			m_NextPiece.Copy(m_JBlock);
			break;
		default:
			m_NextPiece.Copy(m_OBlock);
			break;
		}
		m_NextPiece.m_X = 11; m_NextPiece.m_Y = 0;
	}

	void Input(int Input)
	{
		/** The pieces original x value before input was handled */
		int oldY = m_Piece.m_Y;
		/** The pieces original y value before input was handled */
		int oldX = m_Piece.m_X;
		/** Rotation Direction, 0 == Not Rotated, 1 == Rotated CW, 2 == Rotated CounterCW */
		int rotD = 0;
		switch(Input)
		{
		case 'a':	m_Piece.m_X--;			break;
		case 's':	m_Piece.m_Y++;			break;
		case 'd':	m_Piece.m_X++;			break;
		case 'e':	m_Piece.RotateCW();		
					rotD = 1;				break;
		case 'q':	m_Piece.RotateCCW();	
					rotD = 2;				break;
		case 27 :	m_GameRunning=false;	break;
		case 32 :
			while(IsClipped() == false && CheckSolidify() == false)
			{
				m_Piece.m_Y++;
			}
			Solidify();
			CheckRows();
			SwapPieces();
			break;
		default:							break;
		}
		
		if(m_Piece.m_Y+m_Piece.m_Height > WELL_HEIGHT || m_Piece.m_Y < 0 || m_Piece.m_X < 0 || m_Piece.m_X+m_Piece.m_Width > WELL_WIDTH || IsClipped() == true)
		{
			switch(rotD)
			{
			case 1:
				m_Piece.RotateCCW();	break;
			case 2:
				m_Piece.RotateCW();		break;
			default:
				m_Piece.m_Y = oldY;
				m_Piece.m_X = oldX;		break;
			}
		}
	}
	
	boolean IsClipped()
	{
		for(int y = 0; y < m_Piece.m_Height; ++y)
			for(int x = 0; x < m_Piece.m_Width; ++x)
				if(m_Well[(m_Piece.m_Y+y)][(m_Piece.m_X+x)].m_State==1 && m_Piece.m_Map[y][x]!=0)
					return true;
		return false;
	}

	boolean CheckSolidify()
	{
		for(int y = 0; y < m_Piece.m_Height; ++y)
			for(int x = 0; x < m_Piece.m_Width; ++x)
				if(((m_Piece.m_Y+y) == (WELL_HEIGHT-1)) || (m_Well[(m_Piece.m_Y+y+1)][(m_Piece.m_X+x)].m_State==1 && m_Piece.m_Map[y][x]!=0))
					return true;
		return false;
	}

	void Solidify()
	{
		//TODO figure out why in the world this function is writing to spots in the Well array it shouldn't be
		// I have gone through this step-by-step many times, and determined that for some reason it randomly will write outside
		// in this nested for-loop; the problem shows itself here. The numbers being put into the array to access
		// its state and color never go outside the array, and I have noticed that the glitched array values only change 
		// after setting the m_Color and returning to the outside for-loop to increment 'y'
		for(int y = 0; y < m_Piece.m_Height; ++y)
		{
			for(int x = 0; x < m_Piece.m_Width; ++x)
			{
				if(m_Piece.m_Map[y][x] != 0)
				{
					m_Well[(m_Piece.m_Y+y)][(m_Piece.m_X+x)].m_State = 1;
					m_Well[(m_Piece.m_Y+y)][(m_Piece.m_X+x)].m_Color = m_Piece.m_Color;
				}
			}
		}
	}
	
	void CheckRows()
	{
		boolean RowFull = true;
		for(int y = 0; y < WELL_HEIGHT; ++y)
		{
			for(int x = 0; x < WELL_WIDTH && RowFull == true; ++x)
			{
				if(m_Well[y][x].m_State == 0)
					RowFull = false;
			}
			if(RowFull == true)
			{
				m_Score += 100;
				for(int dy = y; dy > 0; --dy)
				{
					for(int dx = 0; dx < WELL_WIDTH; ++dx)
					{
						m_Well[dy][dx] = m_Well[(dy-1)][dx];
					}
				}
				//Erase the top row since above it would be blank
				for(int dx = 0; dx < WELL_WIDTH; ++dx)
				{
					m_Well[0][dx].m_State = 0;
					m_Well[0][dx].m_Color = 0;
				}
			}
			else
				RowFull = true;
		}
	}
	
	void DrawWellComplete(Graphics g)
	{
		g.setColor(Color.gray);
		g.fillRect(0, 0, WELL_WIDTH * 32, WELL_HEIGHT * 32);
		for(int y = 0; y < WELL_HEIGHT; ++y)
		{
			for(int x = 0; x < WELL_WIDTH; ++x)
			{
				if(m_Well[y][x].m_State == 1)
				{
					switch(m_Well[y][x].m_Color)
					{
					case 1:
						g.setColor(Color.red);		break;
					case 2:
						g.setColor(Color.blue);		break;
					case 3:
						g.setColor(Color.green);	break;
					case 4:
						g.setColor(Color.orange);	break;
					case 5:
						g.setColor(Color.white);	break;
					case 6:
						g.setColor(Color.cyan);		break;
					case 7:
						g.setColor(Color.pink);		break;
						default:
							g.setColor(Color.gray);
					}
					
					g.fillRect(x*32, y*32,32, 32);
				
					if(y > 14)
						g.drawImage(m_HappyFace, x*32, y*32, 32, 32, null);
					else if(y > 9)
						g.drawImage(m_DisappointedFace, x*32, y*32, 32, 32, null);	
					else
						g.drawImage(m_SadFace, x*32, y*32, 32, 32, null);
				}
				
			}
		}
	}
	
	void PrintScore()
	{
		//TODO Score Graphics
//		ColorCursor(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
//		MoveCursor(11, 0);
//		printf("Score = %12d", m_Score);
	}

}
